package com.zh.media.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.zh.common.exception.BusinessException;
import com.zh.common.properties.JwtProperties;
import com.zh.common.utils.RedisUtil;
import com.zh.media.domain.dto.*;
import com.zh.web.domain.media.entity.WorkInfo;
import com.zh.web.domain.media.vo.ArticleVo;
import com.zh.media.mapper.ArticleMapper;
import com.zh.media.service.ArticleService;
import com.zh.media.service.WorkInfoService;
import com.zh.web.base.PageApiResult;
import com.zh.web.domain.media.entity.Article;
import com.zh.web.enums.State;
import com.zh.web.openApi.AuthClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;

import static com.zh.media.Thread.FixedThreadPool.fixedThreadPool;
import static com.zh.media.config.RedisKeyConfig.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author zh
 * @since 2024-10-02
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ArticleServiceImpl extends ServiceImpl<ArticleMapper, Article> implements ArticleService {

    private final ArticleMapper articleMapper;
    private final WorkInfoService workInfoService;
    private final RedisUtil redisUtil;
    private final AmqpTemplate amqpTemplate;
    private final AuthClient authClient;
    private final JwtProperties jwtProperties;

    @Override
    @Transactional
    public String addArticle(AddArticleDto addArticleDto) {
        Article article = BeanUtil.copyProperties(addArticleDto, Article.class);
        article.setSummary(takeSummary(article.getContent()));
        boolean success = articleMapper.insert(article) > 0;
        return success ? "上传成功" : "上传失败";
    }

    @Override
    public String updateArticle(UpdateArticleDto updateArticleDto) {
        Article article = BeanUtil.copyProperties(updateArticleDto, Article.class);
        article.setSummary(takeSummary(article.getContent()));
        return articleMapper.updateById(article) > 0 ? "修改成功" : "修改失败";
    }

    @Override
    public String deleteArticle(Long id) {
        return articleMapper.deleteById(id) > 0 ? "删除成功!" : "删除失败";
    }

    @Override
    public String statusArticle(AuditArticleDto auditArticleDto) {
        Article article = BeanUtil.copyProperties(auditArticleDto, Article.class);
        boolean success = articleMapper.updateById(article) > 0;
        if (success) {
            return "审核成功";
        } else {
            throw new BusinessException("审核失败");
        }
    }

    @Override
    public ArticleVo getDetail(Long id, HttpServletRequest request) {
        Article article = articleMapper.selectById(id);
        if (article == null) {
            return null;
        }
        Long userId;
        String token = request.getHeader(jwtProperties.getTokenName());
        Map<String, Object> userMap = authClient.getSessionByToken(token).getData();
        if (null != userMap) {
            userId = Long.parseLong(userMap.get("id").toString());
        } else {
            userId = null;
        }
        ArticleVo articleVo = BeanUtil.copyProperties(article, ArticleVo.class);
        CompletableFuture<Void> praiseTask = CompletableFuture.runAsync(() -> {
            Integer praiseCount = workInfoService.countPraise(id);
            articleVo.setPraiseCount(praiseCount);
        }, fixedThreadPool);
        CompletableFuture<Void> collectTask = CompletableFuture.runAsync(() -> {
            Integer collectCount = workInfoService.countCollect(id);
            articleVo.setCollectCount(collectCount);
        }, fixedThreadPool);
        CompletableFuture<Void> viewTask = CompletableFuture.runAsync(() -> {
            Integer viewCount = workInfoService.countView(id);
            articleVo.setViewCount(viewCount);
        }, fixedThreadPool);
        CompletableFuture.runAsync(() -> {
            Integer shareCount = workInfoService.countShare(id);
            articleVo.setShareCount(shareCount);
        }, fixedThreadPool);
        CompletableFuture<Void> commentTask = CompletableFuture.runAsync(() -> {
            //回复 todo
//            Integer commentCount = workInfoService.countComment(id);
        }, fixedThreadPool);
        CompletableFuture<Void> meTask = CompletableFuture.runAsync(() -> {
            if (userId != null) {
                //我是否已点赞、收藏
                WorkInfo workInfo = workInfoService.selectByIdAndUserId(id, userId);
                if (null == workInfo) {
                    return;
                }
                articleVo.setIsCollected(State.CONFIRM.equals(workInfo.getCollect()));
                articleVo.setIsPraised(State.CONFIRM.equals(workInfo.getPraise()));
            }
        }, fixedThreadPool);
        CompletableFuture.allOf(praiseTask, collectTask, viewTask, commentTask, meTask).join();
        return articleVo;
    }

    @Override
    public Integer getArticlePraise(Long id) {
        Long setSize = redisUtil.getSetSize(PRISE_SET_KEY_PREFIX + id);
        if (setSize != null) {
            return setSize.intValue();
        }
        return workInfoService.countPraise(id);
    }

    @Override
    public List<ArticleVo> listArticleVoByIds(List<Long> ids) {
        return articleMapper.listArticleVo(ids);
    }

    @Override
    public List<ArticleVo> listArticleAfterTime(LocalDateTime time) {
        return articleMapper.listArticleAfterTime(time);
    }

    @Override
    public PageApiResult<ArticleVo> pageArticle(PageArticleDto pageArticleDto) {
        pageArticleDto.setPageNum(pageArticleDto.getPageNum() < 1L ? 1L : pageArticleDto.getPageNum());
        pageArticleDto.setPageSize(pageArticleDto.getPageSize() < 1L ? 10L : pageArticleDto.getPageSize());
        IPage<ArticleVo> page = new Page<>(pageArticleDto.getPageNum(), pageArticleDto.getPageSize());
        List<ArticleVo> articleList = articleMapper.page(page, pageArticleDto);
        page.setRecords(articleList);
        return PageApiResult
                .<ArticleVo>builder()
                .pageNum(page.getCurrent())
                .pageSize(page.getSize())
                .total(page.getTotal())
                .list(page.getRecords())
                .build();
    }

    @Override
    @Transactional
    public String saveList(List<Article> list) {
        ArticleService proxy = (ArticleService) AopContext.currentProxy();
        proxy.saveBatch(list);
        return "保存成功";
    }

    @Override
    public String praiseArticle(@Valid UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Long articleId = Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Long userId = Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        State praise = Optional.ofNullable(updateWorkInfoDto.getPraise())
                .orElseThrow(() -> new BusinessException("点赞状态不能为空"));
        if (State.CONFIRM.equals(praise)) {
            redisUtil.addSet(PRISE_SET_KEY_PREFIX + articleId, String.valueOf(userId));
        } else {
            redisUtil.removeSet(PRISE_SET_KEY_PREFIX + articleId, String.valueOf(userId));
        }
        sendPraiseMessage(updateWorkInfoDto);
        return State.CONFIRM.equals(praise) ? "点赞成功" : "取消点赞";
    }

    @Override
    public String collectArticle(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Long articleId = Optional.of(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Long userId = Optional.of(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        State collect = Optional.ofNullable(updateWorkInfoDto.getCollect())
                .orElseThrow(() -> new BusinessException("收藏状态不能为空"));
        //2.判断是否收藏过
        if (State.CONFIRM.equals(collect)) {
            redisUtil.addSet(COLLECT_SET_KEY_PREFIX + articleId, String.valueOf(userId));
        } else {
            redisUtil.removeSet(COLLECT_SET_KEY_PREFIX + articleId, String.valueOf(userId));
        }
        sendCollectMessage(updateWorkInfoDto);
        return State.CONFIRM.equals(collect) ? "收藏成功" : "取消收藏";
    }

    @Override
    public Integer updateView(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        Integer incrementView = Optional.ofNullable(updateWorkInfoDto.getViewCount()).orElse(1);
        updateWorkInfoDto.setViewCount(incrementView);
        Integer view = redisUtil.increment(VIEW_COUNT_KEY_PREFIX + updateWorkInfoDto.getWorksId(), incrementView);
        sendViewMessage(updateWorkInfoDto);
        return view;
    }

    @Override
    public Integer updateShare(UpdateWorkInfoDto updateWorkInfoDto) {
        //1.参数校验
        Optional.ofNullable(updateWorkInfoDto.getWorksId())
                .orElseThrow(() -> new BusinessException("文章Id不能为空"));
        Optional.ofNullable(updateWorkInfoDto.getUserId())
                .orElseThrow(() -> new BusinessException("用户Id不能为空"));
        Integer incrementShare = Optional.ofNullable(updateWorkInfoDto.getShareCount())
                .orElse(1);
        updateWorkInfoDto.setShareCount(incrementShare);
        Integer share = redisUtil.increment(SHARE_COUNT_KEY_PREFIX + updateWorkInfoDto.getWorksId(), incrementShare);
        sendShareMessage(updateWorkInfoDto);
        return share;
    }


    private void sendPraiseMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "praise", updateWorkInfoDto);
    }

    private void sendCollectMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "collect", updateWorkInfoDto);
    }

    private void sendViewMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "view", updateWorkInfoDto);
    }

    private void sendShareMessage(UpdateWorkInfoDto updateWorkInfoDto) {
        amqpTemplate.convertAndSend("ls.media.exchange", "share", updateWorkInfoDto);
    }

    private String takeSummary(String content) {
        int endIndex = Math.min(content.length(), 200);
        return content.substring(0, endIndex);
    }
}
